package com.jswdwsx.esign.service

import com.jswdwsx.esign.cloud.enums.FlowStatusEnum
import com.jswdwsx.esign.commom.ResEnum
import com.jswdwsx.esign.config.EsignProperties
import com.jswdwsx.esign.entity.*
import com.jswdwsx.esign.exception.EsignBusinessException
import com.jswdwsx.esign.protocol.EsignApi
import com.jswdwsx.esign.protocol.request.*
import com.jswdwsx.esign.protocol.response.DocsDownloadUrlResp
import com.jswdwsx.esign.protocol.response.QueryFlowResp
import com.jswdwsx.esign.protocol.response.QueryFlowSignFieldsResp
import com.jswdwsx.esign.repo.FlowRepository
import com.jswdwsx.esign.repo.SealRepository
import com.jswdwsx.esign.repo.SignFieldTemplateRepository
import com.jswdwsx.esign.service.enums.SignFieldTypeEnum
import com.jswdwsx.esign.util.jacksonMapper
import com.jswdwsx.esign.util.json
import org.slf4j.LoggerFactory.getLogger
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class FlowService(
    private val signFieldTemplateRepository: SignFieldTemplateRepository,
    private val flowRepository: FlowRepository,
    private val sealRepository: SealRepository,
    private val esignApi: EsignApi,
    private val esignApiWithIdentity: EsignApi,
    private val properties: EsignProperties,
    private val downloadFileService: DownloadFileService
) {

    private val logger = getLogger(FlowService::class.java)

    fun flowInit(
        template: FlowTemplatePO,
        request: RequestPO,
        fileMaps: Map<String, String>,
        signer: UserPO,
        authorizedAccountId: String
    ): FlowPO {
        logger.info("开始执行流程初始化步骤一：根据模板创建签署流程")
        val flow: FlowPO = createSignFlow(template, request)

        logger.info("开始执行流程初始化步骤二：添加文件至签署流程")
        addFile2Flow(flow.esignFlowId, fileMaps.values)

        logger.info("开始执行流程初始化步骤三：添加签署区至签署流程")
        addSignFields2Flow(flow.esignFlowId, fileMaps, signer, authorizedAccountId, request)

        logger.info("开始执行流程初始化步骤四：开始签署流程")
        startFlow(flow.esignFlowId)
        return flow
    }

    /**
     * 使用一步发起签署接口快速发起签署
     * 接口太大了，看着太乱，暂不启用
     */
    fun fastFlowInit(
        template: FlowTemplatePO,
        request: RequestPO,
        fileMaps: Map<String, String>,
        signer: UserPO,
        authorizedAccountId: String
    ): FlowPO {
        logger.info("开始执行：一步发起签署")

        val docs = fileMaps.values.map { Doc(it) }.toTypedArray()
        val flowInfo = createFlowInfo(request, template)

        val fieldTemplateList = signFieldTemplateRepository.findAllByFlowTemplateId(request.templateId)

        val map: MutableMap<OneStepSigner, MutableList<OneStepSignField>> = HashMap()
        fieldTemplateList.forEach { signFieldTemplate: SignFieldTemplatePO ->
            val platformSign = signFieldTemplate.type == SignFieldTypeEnum.PLATFORM_SIGN
            val oneStepSignerAccount = if (platformSign) null else (
                    if (signFieldTemplate.type == SignFieldTypeEnum.AUTO_SIGN) {
                        OneStepSignerAccount(
                            signerAccountId = signFieldTemplate.signerAccountId!!,
                            authorizedAccountId = signFieldTemplate.authorizedAccountId!!
                        )
                    } else {
                        OneStepSignerAccount(
                            signerAccountId = signer.accountId,
                            authorizedAccountId = authorizedAccountId
                        )
                    })
            val oneStepSigner = OneStepSigner(
                platformSign = platformSign,
                signOrder = signFieldTemplate.order,
                signerAccount = oneStepSignerAccount
            )

            val posBean =
                OneStepPosBean(signFieldTemplate.posPage, signFieldTemplate.posX, signFieldTemplate.posY)

            // 是否指定了印章
            val esignSealId: String? = getEsignSealId(signFieldTemplate, request)

            val oneStepSignField = OneStepSignField(
                autoExecute = signFieldTemplate.type == SignFieldTypeEnum.PLATFORM_SIGN,
                actorIndentityType = signFieldTemplate.actorIndentityType,
                fileId = fileMaps.getValue(signFieldTemplate.fileTemplateId),
                sealId = esignSealId,
                signType = signFieldTemplate.signType,
                sealType = signFieldTemplate.sealType,
                posBean = posBean,
                width = signFieldTemplate.width
            )
            if (map[oneStepSigner] == null) {
                map[oneStepSigner] = mutableListOf(oneStepSignField)
            } else {
                map[oneStepSigner]!!.add(oneStepSignField)
            }
        }

        val createFlowOneStepReq = CreateFlowOneStepReq(
            docs = docs,
            flowInfo = flowInfo,
            signers = map.map { (key, value) ->
                key.signfields = value.toTypedArray()
                key
            }.toTypedArray()
        )

        logger.info("调用E签宝一步发起签署接口 ,createFlowOneStepReq:${createFlowOneStepReq}")

        val esignResp = esignApi.createFlowOneStep(createFlowOneStepReq)

        logger.info("E签宝一步发起签署接口响应 ,esignResp:$esignResp")
        if (esignResp.isSuccess) {
            val data = esignResp.data
            if (null != data) {
                return flowRepository.save(
                    FlowPO(
                        requestId = request.id,
                        esignFlowId = data.flowId,
                        status = FlowStatusEnum.NEW,
                        request = request,
                        ossFileUrl = null,
                        version = 1
                    )
                ).also {
                    logger.info("flow:${it.json(jacksonMapper)}")
                }
            }
        } else {
            logger.error("创建签署流程失败,code:${esignResp.code},message:${esignResp.message}")
        }
        throw EsignBusinessException(ResEnum.CallEsignFlowCreateFail)
    }

    private fun getEsignSealId(
        signFieldTemplate: SignFieldTemplatePO,
        request: RequestPO
    ): String? {
        return if (signFieldTemplate.inputSealKey != null) {
            val sealId = request.seals[signFieldTemplate.inputSealKey!!]
                ?: throw EsignBusinessException(ResEnum.SpecifySealSignNeedSealId)
            val seal = sealRepository.findByIdOrNull(sealId)
                ?: throw EsignBusinessException(ResEnum.SealDoNotExist)
            seal.esignSealId
        } else {
            null
        }
    }

    fun createSignFlow(flowTemplatePO: FlowTemplatePO, request: RequestPO): FlowPO {
        val flowReq = createFlowReq(request, flowTemplatePO)

        val esignResp = if (flowTemplatePO.needIdentity) {
            esignApiWithIdentity.createFlow(flow = flowReq)
        } else {
            esignApi.createFlow(flow = flowReq)
        }
        val data = esignResp.data(ResEnum.CallEsignFlowCreateFail)
        return flowRepository.save(
            FlowPO(
                requestId = request.id,
                esignFlowId = data.flowId,
                status = FlowStatusEnum.NEW,
                request = request,
                ossFileUrl = null,
                version = 1
            )
        ).also {
            logger.info("flow:${it.json(jacksonMapper)}")
        }
    }

    private fun createFlowInfo(
        request: RequestPO,
        flowTemplatePO: FlowTemplatePO
    ): FlowInfo {
        val noticeUrl = properties.noticeUrl
        val redirectUrl = request.redirectUrl ?: ""
        return FlowInfo(
            autoArchive = true,
            businessScene = flowTemplatePO.scene,
            configInfo = ConfigInfo(
                noticeDeveloperUrl = noticeUrl,
                redirectUrl = redirectUrl,
                signPlatform = flowTemplatePO.signPlatform.value
            ),
            contractValidity = flowTemplatePO.contractValidity,
            contractRemind = flowTemplatePO.contractRemind,
            signValidity = flowTemplatePO.signValidity
        )
    }

    private fun createFlowReq(
        request: RequestPO,
        flowTemplatePO: FlowTemplatePO
    ): FlowReq {
        val noticeUrl = properties.noticeUrl
        val redirectUrl = request.redirectUrl ?: ""
        return FlowReq(
            autoArchive = true,
            businessScene = flowTemplatePO.scene,
            configInfo = ConfigInfo(
                noticeDeveloperUrl = noticeUrl,
                redirectUrl = redirectUrl,
                signPlatform = flowTemplatePO.signPlatform.value,
                noticeType = flowTemplatePO.noticeType?.value
            ),
            contractValidity = flowTemplatePO.contractValidity,
            contractRemind = flowTemplatePO.contractRemind,
            signValidity = flowTemplatePO.signValidity
        )
    }

    fun addFile2Flow(flowId: String, fileIds: Collection<String>) {
        val docs = fileIds.map { Doc(it) }

        val req = AddDocs2FlowReq(docs.toTypedArray())
        val body = esignApi.addDoc2Flow(flowId, req)
        if (body.isSuccess) {
            logger.info("添加文件至签署流程成功,esignFlowId:$flowId,fileId:$fileIds")
            return
        } else {
            logger.error("添加文件至签署流程失败,esignFlowId:$flowId,code:${body.code},message:${body.message}")
        }

        throw EsignBusinessException(ResEnum.CallEsignFlowAddFileFail)
    }

    fun addSignFields2Flow(
        flowId: String,
        fileMaps: Map<String, String>,
        signer: UserPO,
        authorizedAccountId: String,
        requestPO: RequestPO
    ) {
        val fieldList = signFieldTemplateRepository.findAllByFlowTemplateId(requestPO.templateId)
        fieldList.forEach { template: SignFieldTemplatePO ->
            val posBean =
                PosBean(template.posPage, template.posX, template.posY, template.width, template.addSignTime)
            when (template.type) {
                // 添加平台自动签署区
                SignFieldTypeEnum.PLATFORM_SIGN -> {
                    addPlatformSign(fileMaps, template, posBean, flowId)
                }
                // 添加第三方授权静默签署区
                SignFieldTypeEnum.AUTO_SIGN -> {
                    addAutoSign(template, requestPO, fileMaps, posBean, flowId)
                }
                //添加用户手动盖章签署区
                SignFieldTypeEnum.HAND_SIGN -> {
                    addHandSign(template, requestPO, fileMaps, signer, authorizedAccountId, posBean, flowId)
                }
            }
        }
    }

    private fun addHandSign(
        template: SignFieldTemplatePO,
        requestPO: RequestPO,
        fileMaps: Map<String, String>,
        signer: UserPO,
        authorizedAccountId: String,
        posBean: PosBean,
        flowId: String
    ) {
        // 是否指定了印章
        val esignSealId: String? = getEsignSealId(template, requestPO)

        val signField = HandSignField(
            fileId = fileMaps.getValue(template.fileTemplateId),
            signerAccountId = signer.accountId,
            actorIndentityType = requestPO.identityType.value,
            authorizedAccountId = authorizedAccountId,
            order = template.order,
            assignedPosBean = template.assignedPosBean!!,
            posBean = posBean,
            sealType = template.sealType,
            signType = template.signType,
            sealId = esignSealId
        )
        val addHandSignFields2FlowReq = AddHandSignFields2FlowReq(arrayOf(signField))

        val esignResp = esignApi.addHandSign2Flow(
            flowId = flowId,
            addHandSignFields2FlowReq = addHandSignFields2FlowReq
        )
        val isSuccess = esignResp.isSuccess
        logger.info("添加用户手动盖章签署区${if (isSuccess) "成功" else "失败"},接口响应：$esignResp")
        if (!isSuccess) {
            throw EsignBusinessException(ResEnum.CallAddSignFieldFail, esignResp.message)
        }
    }

    private fun addAutoSign(
        template: SignFieldTemplatePO,
        requestPO: RequestPO,
        fileMaps: Map<String, String>,
        posBean: PosBean,
        flowId: String
    ) {
        // 判断签署区是否使用入参中的sealId
        val esignSealId: String
        val accountId: String?

        if (template.inputSealKey != null) {
            val sealId = requestPO.seals[template.inputSealKey!!] ?: throw EsignBusinessException(
                ResEnum.SpecifySealSignNeedSealId
            )
            val seal = sealRepository.findByIdOrNull(sealId)
                ?: throw EsignBusinessException(ResEnum.SealDoNotExist)
            esignSealId = seal.esignSealId
            accountId = seal.owner
        } else {
            esignSealId = template.sealId!!
            accountId = template.authorizedAccountId
        }

        val signField = AutoSignField(
            fileId = fileMaps.getValue(template.fileTemplateId),
            authorizedAccountId = accountId,
            order = template.order,
            posBean = posBean,
            sealId = esignSealId,
            signType = template.signType
        )

        logger.info("添加第三方授权静默签署区接口，调用参数$signField")
        val addAutoSignFields2FlowReq = AddAutoSignFields2FlowReq(arrayOf(signField))

        val esignResp = esignApi.addAutoSign2Flow(
            flowId = flowId,
            addAutoSignFields2FlowReq = addAutoSignFields2FlowReq
        )
        val isSuccess = esignResp.isSuccess
        logger.info("添加第三方授权静默签署区${if (isSuccess) "成功" else "失败"},接口响应：$esignResp")
        if (!isSuccess) {
            throw EsignBusinessException(ResEnum.CallAddSignFieldFail, esignResp.message)
        }
    }

    private fun addPlatformSign(
        fileMaps: Map<String, String>,
        template: SignFieldTemplatePO,
        posBean: PosBean,
        flowId: String
    ) {
        val signField = PlatformSignField(
            fileMaps.getValue(template.fileTemplateId),
            template.order,
            posBean,
            template.sealId!!,
            template.signType
        )
        val addPlatformSignFields2FlowReq = AddPlatformSignFields2FlowReq(arrayOf(signField))
        val esignResp = esignApi.addPlatformSign2Flow(
            flowId = flowId,
            addPlatformSignFields2FlowReq = addPlatformSignFields2FlowReq
        )
        val isSuccess = esignResp.isSuccess
        logger.info("添加平台自动签署区${if (isSuccess) "成功" else "失败"},接口响应：$esignResp")
        if (!isSuccess) {
            throw EsignBusinessException(ResEnum.CallAddSignFieldFail, esignResp.message)
        }
    }

    fun startFlow(flowId: String) {
        val esignResp = esignApi.startFlow(flowId)
        if (esignResp.isSuccess) {
            logger.info("开启签署流程成功,esignFlowId:$flowId")
            return
        }
        logger.error("开启签署流程失败,esignFlowId:$flowId,code:${esignResp.code},message:${esignResp.message}")
        throw EsignBusinessException(ResEnum.CallEsignFlowStartFail)
    }

    fun getSignUrl(flowId: String, signer: UserPO, orgId: String?): String? {

        val esignResp = esignApi.getSignUrl(flowId, signer.accountId, orgId)
        logger.info("获取签署url响应:$esignResp")
        val (shortUrl, url) = esignResp.data(ResEnum.CallGetSignUrlFail)
        return shortUrl ?: url
    }

    fun queryFlow(id: String): QueryFlowResp {
        return esignApi.getFlow(id).also {
            logger.info("e签宝签署流程查询接口响应：$it")
        }.data ?: throw Exception("查询失败")
    }

    fun queryFlowSignFields(id: String): QueryFlowSignFieldsResp {
        return esignApi.getFlowSignFields(id).also {
            logger.info("e签宝签署流程签署区查询接口响应：$it")
        }.data ?: throw Exception("查询失败")
    }

    fun getDocsDownloadUrl(flowId: String): DocsDownloadUrlResp? {
        val esignResp = esignApi.docsDownloadUrl(flowId)
        if (esignResp.isSuccess) {
            logger.info("获取流程文档下载地址,esignFlowId:$flowId")
            return esignResp.data
        } else {
            logger.error("获取流程文档下载地址,esignFlowId:$flowId,code:${esignResp.code},message:${esignResp.message}")
        }
        return null
    }

    fun downloadFlowFile(flowId: String): List<com.jswdwsx.esign.protocol.response.Doc>? {
        return getDocsDownloadUrl(flowId)?.docs?.map {
            it.apply {
                fileUrl = downloadFileService.storedFile2Oss(fileName, fileUrl)
            }
        }
    }
}
